import { pick, unset } from 'lodash';

import {
    FindOptionsUtils,
    FindTreeOptions,
    SelectQueryBuilder,
    TreeRepository,
    TreeRepositoryUtils,
} from 'typeorm';
import { CommentEntity } from '../Entity';
import { CustomRepositoryDecorator } from 'src/database/decorator/repository.decorator';


type FindCommentTreeOptions = FindTreeOptions & {
    addQuery?: (query: SelectQueryBuilder<CommentEntity>) => SelectQueryBuilder<CommentEntity>;
};

@CustomRepositoryDecorator(CommentEntity)
export class CommentRepository extends TreeRepository<CommentEntity> {
    /**
     * 构建基础查询器
     */
    buildBaseQB(qb: SelectQueryBuilder<CommentEntity>): SelectQueryBuilder<CommentEntity> {
        return qb
            .leftJoinAndSelect(`comment.parent`, 'parent')
            .leftJoinAndSelect(`comment.post`, 'post')
            .orderBy('comment.createdAt', 'DESC');
    }

    /**
     * 查询树
     * @param options
     */
    async findTree(options: FindCommentTreeOptions = {}) {
        options.relations = ['parent', 'children'];
        const roots = await this.findRoots(options);
        await Promise.all(roots.map((root) => this.findDescendantsTree(root, options)));
        return roots;
    }
    /**
     * 查询顶级评论
     * 
     */

    createDtsQueryBuilder(
        closureTableAlias: string,
        entity: CommentEntity,
        options: FindCommentTreeOptions = {},
    ): SelectQueryBuilder<CommentEntity> {
        const { addQuery } = options;
        const qb = this.buildBaseQB(
            super.createDescendantsQueryBuilder('comment', closureTableAlias, entity),
        );
        return addQuery ? addQuery(qb) : qb;
    }

    findRoots(options: FindCommentTreeOptions = {}) {
        const { addQuery, ...rest } = options;
        const escapeAlias = (alias: string) => this.manager.connection.driver.escape(alias);
        const escapeColumn = (column: string) => this.manager.connection.driver.escape(column);

        const joinColumn = this.metadata.treeParentRelation!.joinColumns[0];
        const parentPropertyName = joinColumn.givenDatabaseName || joinColumn.databaseName;

        let qb = this.buildBaseQB(this.createQueryBuilder('comment'));
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, rest);
        qb.where(`${escapeAlias('comment')}.${escapeColumn(parentPropertyName)} IS NULL`);
        qb = addQuery ? addQuery(qb) : qb;
        return qb.getMany();
    }

    /**
     * 祖先查询器
     */
    findAncestors(entity: CommentEntity, option?: FindTreeOptions) {
        //第一步祖先查询器
        let qb = this.createAncestorsQueryBuilder('comment', 'treeClosure', entity)
        //将查询条件进行关联
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, option)
        //进行默认排序
        qb.orderBy('comment.createdAt', 'ASC')
        //返回查询出来的数据
        return qb.getMany()

    }
    /**
     * 后代查询器
     */

    async findDescendantsTree(
        entity: CommentEntity,
        options: FindCommentTreeOptions = {},
    ): Promise<CommentEntity> {
        const qb: SelectQueryBuilder<CommentEntity> = this.createDtsQueryBuilder(
            'treeClosure',
            entity,
            options,
        );
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, pick(options, ['relations', 'depth']));

        const entities = await qb.getRawAndEntities();
        
        const relationMaps = TreeRepositoryUtils.createRelationMaps(
            this.manager,
            this.metadata,
            'comment',
            entities.raw,
        );
        TreeRepositoryUtils.buildChildrenEntityTree(
            this.metadata,
            entity,
            entities.entities,
            relationMaps,
            {
                depth: -1,
                ...pick(options, ['relations']),
            },
        );

        return entity;
    }

    //数据扁平化
    async toFlatTrees(tree: CommentEntity[], depth = 1, parent: CommentEntity | null = null) {
        //定义接收的数组
        const flatTree: Omit<CommentEntity, 'children' | 'hasId' | 'save' | 'remove' | 'softRemove' | 'recover' | 'reload'>[] = [];

        for (let item of tree) {
            item.depth = depth
            item.parent = parent
            let Flat = { ...item }
            delete Flat.children
            flatTree.push(Flat)

            //进行递归
            if (item.children.length) {
                let flattenedChildren: any = await this.toFlatTrees(item.children, depth + 1, item)
                flatTree.push(flattenedChildren)
            }
            return flatTree
        }

    }
}
